#ifndef _DOCUMENTVECTOR_H_
#define _DOCUMENTVECTOR_H_

#include <iostream>
#include <vector>
#include <cmath>
#include <string>

class DocumentVector 
{	
private:
	int* frequencies; 	// TODO: using a vector instead of an array could be useful!
	int size;
	int num_words;
	int max_word_index;
	int min_word_index;
	std::string identifier;
	
	friend class DVReference;
	class DVReference {
	private:
		DocumentVector &dv;
		int index;
		
		DVReference(DocumentVector &dv_, int index_) : dv(dv_), index(index_) {}
		friend class DocumentVector;
		
	public:		
		// cast to int
		operator int () const
		{
			if (index >= dv.size)
				return 0;
			else
				return dv.frequencies[index];
		}

		// preincrement operator
	 	const DVReference& operator ++ () const
		{
			dv.add_word_index(index);
			return *this;
		}
		
		// postincrement operator
		int operator++ (int) const
		{
			int old_value = (int) (*this);
			dv.add_word_index(index);
			return old_value;
		}

		// += operator
		const DVReference& operator += (unsigned int n) const 
		{
			dv.add_word_index(index, n);
			return *this;
		}
	};

	void add_word_index(int index, unsigned int n = 1) 
	{
		assert(index < size);

		if(max_word_index < index) 
			max_word_index = index;

		if(min_word_index > index)
			min_word_index = index;

		if(frequencies[index] == 0 && n > 0)
			num_words++;

		frequencies[index]+= n;
	}
	
public:
 	
	// Default constructor
	DocumentVector(int size_ = 0, std::string identifier_ = "unknown") :
		frequencies(new int[size_]),
		size(size_),
		num_words(0),
		max_word_index(0),
		min_word_index(size),
		identifier(identifier_)
	{
		for(int i = 0; i < size; i++)
			frequencies[i] = 0;
	}
	
	// Copy constructor
	DocumentVector(const DocumentVector &dv) :
		frequencies (new int[dv.size]),
		size (dv.size),
		num_words (dv.num_words),
		max_word_index (dv.max_word_index),
		min_word_index (dv.min_word_index),
		identifier(dv.identifier)
	{
		for(int i = 0; i < size; i++)
			frequencies[i] = dv.frequencies[i];
	}
	
	// Destructor
	~DocumentVector()
	{
		delete [] frequencies;
	}
	
	// Assignment operator
	DocumentVector& operator = (const DocumentVector &dv)
	{
		if(this != &dv) {
			delete [] frequencies;

			frequencies = new int[dv.size];
			size = dv.size;
			num_words = dv.num_words;
			max_word_index = dv.max_word_index;
			min_word_index = dv.min_word_index;
			identifier = dv.identifier;
			
			for(int i = 0; i < size; i++)
				frequencies[i] = dv.frequencies[i];
		}

		return *this;
	}
	
	int operator[](int index) const
	{
		if (index >= size)
			return 0;
		else
			return frequencies[index];
	}
	
	DVReference operator[](int index)
	{
		return DVReference (*this, index);
	}
	
	// << and >> operators	
	friend std::ostream& operator << (std::ostream &os, const DocumentVector &dv)
	{
		os << dv.identifier;
		os << ' ' << dv.size;
		os << ' ' << dv.num_words;
		os << ' ' << dv.min_word_index;
		os << ' ' << dv.max_word_index;
		for(int i = 0; i < dv.size; i++)
			os << ' ' << dv[i];
			
		return os;
	}

	friend std::istream& operator >> (std::istream& is, DocumentVector& dv) 
	{
		is >> dv.identifier;
		is >> dv.size;
		is >> dv.num_words;
		is >> dv.min_word_index;
		is >> dv.max_word_index;
		dv.frequencies = new int[dv.size];
		for(int i = 0; i < dv.size; i++)
			is >> dv.frequencies[i];
			
		return is;
	}
	
	// getters and setters
	int get_max_word_index() const
	{
		return max_word_index;
	}

	int get_min_word_index() const
	{
		return min_word_index;
	}

	int get_num_words() const
	{
		return num_words;
	}
	
 	std::string get_identifier() 
	{
		return identifier;
	}
};

#endif /* _DOCUMENTVECTOR_H_ */
